package icraf.carbon.gui;

import static icraf.carbon.gui.MenuFactory.MenuProperty.APP_CLOSE;
import static icraf.carbon.gui.MenuFactory.MenuProperty.APP_EXIT;
import static icraf.carbon.gui.MenuFactory.MenuProperty.APP_FILE;
import static icraf.carbon.gui.MenuFactory.MenuProperty.APP_HELP;
import static icraf.carbon.gui.MenuFactory.MenuProperty.APP_HELP_OPEN;
import static icraf.carbon.gui.MenuFactory.MenuProperty.APP_SAVE;
import static icraf.carbon.gui.MenuFactory.MenuProperty.APP_SAVE_ALL;
import static icraf.carbon.gui.MenuFactory.MenuProperty.APP_SET;
import static icraf.carbon.gui.MenuFactory.MenuProperty.APP_TITLE;
import static icraf.carbon.gui.MenuFactory.MenuProperty.APP_WRITE;
import static icraf.carbon.gui.MenuFactory.MenuProperty.FILE_EXIT;
import static icraf.carbon.gui.MenuFactory.MenuProperty.FILE_NEW;
import static icraf.carbon.gui.MenuFactory.MenuProperty.FILE_OPEN;
import static icraf.carbon.gui.MenuFactory.MenuProperty.FILE_SAVE;
import static icraf.carbon.gui.MenuFactory.MenuProperty.FILE_SAVE_AS;
import static icraf.carbon.gui.MenuFactory.MenuProperty.FILE_SAVE_OUTPUT;
import static icraf.carbon.gui.MenuFactory.MenuProperty.GENERAL_ABOUT;
import static icraf.carbon.gui.MenuFactory.MenuProperty.GENERAL_HELP;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_CLOSE;
import icraf.carbon.core.Config;
import icraf.carbon.core.Project;
import icraf.carbon.core.ProjectIO;
import icraf.carbon.gui.MenuFactory.MenuProperty;
import icraf.common.gui.MenuAdapter;
import icraf.common.gui.MenuGenerator;
import icraf.common.gui.MenuGenerator.IconModel;
import icraf.common.util.CommonFileFilter;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Locale;

import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.WindowConstants;

import com.vlsolutions.swing.docking.DockableState;
import com.vlsolutions.swing.docking.DockableState.Location;
import com.vlsolutions.swing.docking.DockingConstants;
import com.vlsolutions.swing.docking.DockingDesktop;
import com.vlsolutions.swing.docking.RelativeDockablePosition;
import com.vlsolutions.swing.docking.event.DockableSelectionEvent;
import com.vlsolutions.swing.docking.event.DockableSelectionListener;

public class AbacusMain {
	public class StatusBarPanel extends JPanel implements StatusBar {
		private static final long serialVersionUID = -3581643090590149540L;
		private JLabel label = new JLabel();

		public StatusBarPanel() {
			this.add(label, new FlowLayout(FlowLayout.RIGHT, 0, 0) );
			this.setPreferredSize(new Dimension(200, 20));
		}

		@Override
		public void setText(String str) {
			label.setText(str);
		}

	}

	//private String helpFilePath = "/abacus_manual.pdf";
	private String helpFilePath = "/help/index.html";
//	private PDFViewer pdfViewer;

	private class Listener implements ActionListener, ProjectDockListener,
			DockableSelectionListener {

		
		@Override
		public void actionPerformed(ActionEvent e) {
			String action = e.getActionCommand();
			if (action.equals(FILE_EXIT.toString())) {
				exit();
			} else if (action.equals(FILE_NEW.toString())) {
				createProject();
			} else if (GENERAL_ABOUT.isAction(e)) {
				if (aboutDialog == null) {
					AboutAbacus aa = new AboutAbacus(true);
					aboutDialog = aa.createDialog(frame,
							ModalityType.APPLICATION_MODAL);
				}
				Point p = desktop.getLocationOnScreen();
				int x = (desktop.getWidth() - aboutDialog.getWidth()) / 2;
				aboutDialog.setLocation(p.x + x, p.y);
				aboutDialog.setVisible(true);
			} else if (FILE_SAVE.isAction(e)) {
				Project project = projectDock.getSelectedProject();
				if (project == null)
					return;
				saveProject(project, false);
			} else if (FILE_SAVE_AS.isAction(e)) {
				Project project = projectDock.getSelectedProject();
				if (project == null)
					return;
				saveProject(project, true);
			} else if (FILE_SAVE_OUTPUT.isAction(e)) {
				Project project = projectDock.getSelectedProject();
				if (project == null)
					return;
				saveCalculationOutput(project);
			} else if (FILE_OPEN.isAction(e)) {
				loadProject(null);
			} else if (MenuProperty.PROJECT_CLOSE.isAction(e)) {
				removeProject(projectDock.getSelectedProject(),
						(Component) e.getSource());
			} else if (GENERAL_HELP.isAction(e)) {
				URL u = MenuFactory.class.getResource(helpFilePath);
				File helpFile;
				try {
					helpFile = new File(u.toURI());
					
					if (!helpFile.canRead()) {
						JOptionPane.showMessageDialog(desktop,
								APP_HELP.getLabel());
						return;
					}
					//openPDF(helpFile);
										
					int ret = JOptionPane.showConfirmDialog((Component)e.getSource(),
							APP_HELP_OPEN.getAccessibleDescription(), APP_HELP_OPEN.getLabel(), JOptionPane.YES_NO_OPTION);
					if (ret == JOptionPane.YES_OPTION)
						openExtFile(helpFile);
				} catch (Exception exc) {
					//exc.printStackTrace();
					JOptionPane.showMessageDialog(desktop,
							APP_HELP.getAccessibleDescription());
					
					
				}
			}

		}

		@Override
		public void projectDoubleClicked(ProjectPanelEvent e) {
			showPropertyDock(e.getChildObject(), e.getProperties(),
					e.getProject());

		}

		@Override
		public void projectPopupTriggered(ProjectPanelEvent e,
				Component invoker, int x, int y) {
			if (e.getProject() == null) {
				// popupFile.show(invoker, x, y);
			} else {
				popMenu.show(invoker, x, y);
			}

		}

		@Override
		public void projectSelectionChanged(ProjectPanelEvent e) {
			Project p = e.getProject();
			String title = AboutAbacus.appTitle;
			if (p != null) {
				switchPropertyDisplay(e.getChildObject(), e.getProperties(), p);
				title += " - [" + p.getLabel() + "] ";
				File f = projectManager.getFile(p);
				if (f != null)
					title += f.getPath();
			}
			frame.setTitle(title);
		}

		@Override
		public void selectionChanged(DockableSelectionEvent e) {
		}

	}

//	public void openPDF(File document) throws IOException {
//		if (pdfViewer == null) {
//			pdfViewer = new PDFViewer(true);
//			pdfViewer.setIconImage(APP_TITLE.getIcon(IconModel.ICON16)
//					.getImage());
//
//		}
//		pdfViewer.openFile(document);
//		pdfViewer.setTitle(APP_MANUAL.getAccessibleDescription());
//		pdfViewer.setVisible(true);
//
//	}

	public static void setUIFont(javax.swing.plaf.FontUIResource f) {
		Enumeration<Object> keys = UIManager.getDefaults().keys();
		while (keys.hasMoreElements()) {
			Object key = keys.nextElement();
			Object value = UIManager.get(key);
			if (value != null
					&& value instanceof javax.swing.plaf.FontUIResource)
				UIManager.put(key, f);
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String langID = null;
		File fileOpen = null;
		if (args.length > 0) {
			for (int i = 0; i < args.length; i++) {
				if (args[i].equalsIgnoreCase("-lang")) {
					if (args.length > i + 1) {
						langID = args[i + 1];
						i++;
					}
				} else {
					File f = new File(args[i]);
					if (f.isFile()) {
						fileOpen = f;
					}
				}
			}
		}
		if (langID != null) {
			Locale loc = new Locale(langID);
			MenuFactory.locale = loc;
		}

		try {
			UIManager
					.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
			// javax.swing.plaf.metal.MetalLookAndFeel
			// com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel
			// com.sun.java.swing.plaf.motif.MotifLookAndFeel
			// com.sun.java.swing.plaf.windows.WindowsLookAndFeel
			// com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel
		} catch (Exception e) {
		}

		// setUIFont (new
		// javax.swing.plaf.FontUIResource("Courier New",Font.PLAIN,12));
		

		final JFrame frame = new JFrame();
		AbacusMain mainPanel = new AbacusMain(frame);
		if (fileOpen != null) {
			mainPanel.loadProject(fileOpen);
		}

		frame.getContentPane().add(mainPanel.getDesktopPanel(),
				BorderLayout.CENTER);

		frame.setJMenuBar(mainPanel.createMenuBar());
		frame.validate();
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				frame.setVisible(true);
			}
		});

	}

	private File activeFile;
	private CommonFileFilter defaultFileFilter;
	private DockingDesktop desktop = new DockingDesktop();
	private JFileChooser fileChooser = new JFileChooser();
	private JFileChooser fileChooserOutput = new JFileChooser();
	private JFrame frame;
	private Listener listener = new Listener();
	private MenuAdapter[] menuFile = { FILE_NEW, null, FILE_OPEN, FILE_SAVE,
			FILE_SAVE_AS, null, FILE_SAVE_OUTPUT, null, FILE_EXIT };
	private MenuAdapter[] menuHelp = { GENERAL_HELP, GENERAL_ABOUT };
	private MenuAdapter[] menuPop = { FILE_NEW, null, FILE_OPEN, FILE_SAVE,
			FILE_SAVE_AS, null, FILE_SAVE_OUTPUT, null, PROJECT_CLOSE };
	private MenuAdapter[] menuToolBar = { FILE_NEW, null, FILE_OPEN, FILE_SAVE,
			FILE_SAVE_AS, null, FILE_SAVE_OUTPUT, null, PROJECT_CLOSE, null, GENERAL_HELP };
	private JPopupMenu popMenu = new JPopupMenu();
	private int projectCounter = 1;
	private ProjectTreePanel projectDock;
	private ProjectManager projectManager = new ProjectManager();
	private CardPanel propertyDock;
	private CommonFileFilter textFileFilter;
	private JToolBar toolBar = new JToolBar();
	private JDialog aboutDialog;
	private StatusBarPanel statusBar;

	public AbacusMain(JFrame frame) {
		this.frame = frame;
		if (frame != null) {
			Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

			final int width = Math.min(800, screenSize.width);
			final int height = Math.min(600, screenSize.height);
			this.frame.setBounds((screenSize.width - width) / 2,
					(screenSize.height - height) / 2, width, height);

			frame.setIconImage(APP_TITLE.getIcon(IconModel.ICON16).getImage());
			frame.setTitle(AboutAbacus.appTitle);
			frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
			frame.addWindowListener(new WindowAdapter() {
				@Override
				public void windowClosing(WindowEvent e) {
					exit();
					super.windowClosing(e);
				}

			});
		}
		initPanel();
		initMenu();
		
		projectManager.getConfig().loadConfig();

		defaultFileFilter = new CommonFileFilter(APP_TITLE.getLabel()
				+ " Project File (*.car)", "car");
		textFileFilter = new CommonFileFilter("Text File (*.txt)", "txt");
		CommonFileFilter ext = new CommonFileFilter("Excel File (*.xls)", "xls");

		fileChooser.addChoosableFileFilter(defaultFileFilter);
		fileChooser.addChoosableFileFilter(textFileFilter);
		fileChooser.addChoosableFileFilter(ext);
		fileChooser.setFileFilter(defaultFileFilter);

		fileChooserOutput.addChoosableFileFilter(textFileFilter);
		fileChooserOutput.addChoosableFileFilter(ext);
		fileChooserOutput.setFileFilter(textFileFilter);
		fileChooserOutput.setDialogTitle(FILE_SAVE_OUTPUT.getLabel());
	}

	public JMenuBar createMenuBar() {

		JMenuBar m = new JMenuBar();
		JMenu fileM = m.add(new JMenu(MenuProperty.FILE.getLabel()));
		fileM = MenuGenerator.createMenuItem(fileM, menuFile, listener);

		JMenu helpM = m.add(new JMenu(MenuProperty.GENERAL_HELP.getLabel()));
		helpM = MenuGenerator.createMenuItem(helpM, menuHelp, listener);
		return m;
	}

	public void createProject() {
		String inp = JOptionPane.showInputDialog(desktop,
				APP_SET.getAccessibleDescription() + ":", APP_SET.getLabel()
						+ projectCounter);
		if (inp == null)
			return;
		Project p = new Project();
		p.setLabel(inp);
		projectCounter++;
		projectManager.add(p);
		DockableState state = desktop.getDockableState(propertyDock);
		if (state.isMaximized()) {
			desktop.restore(propertyDock);
		}
	}

	private void exit() {
		int response = JOptionPane.showConfirmDialog(desktop,
				APP_EXIT.getAccessibleDescription(), APP_EXIT.getLabel(),
				JOptionPane.YES_NO_OPTION);
		if (response == JOptionPane.YES_OPTION) {
			if(activeFile != null)
				Config.ConfigItem.ACTIVE_FOLDER.setValue(activeFile.getAbsolutePath());
			projectManager.getConfig().saveConfig();
			System.exit(0);
		}
	}

	private Component getDesktopPanel() {
		return desktop;
	}

	private void initMenu() {
		popMenu = MenuGenerator.createMenuItem(popMenu, menuPop, listener);
		toolBar = MenuGenerator.createMenuItem(toolBar, menuToolBar, listener);
		frame.getContentPane().add(toolBar, BorderLayout.NORTH);
		frame.getContentPane().add(statusBar, BorderLayout.SOUTH);
	}

	private void initPanel() {
		projectDock = new ProjectTreePanel(projectManager, this);
		projectDock.addProjectDockListener(listener);
		propertyDock = new CardPanel(projectDock);
		desktop.addDockable(propertyDock);
		desktop.split(propertyDock, projectDock, DockingConstants.SPLIT_LEFT);
		desktop.setDockableWidth(projectDock, 0.25);
		desktop.maximize(propertyDock);
		
		statusBar = new StatusBarPanel();
	}

	private void loadProject(File fInput) {
		Project p = null;
		if (activeFile != null) {
			fileChooser.setCurrentDirectory(activeFile);
		} else if (Config.ConfigItem.ACTIVE_FOLDER.getValueString() != null) {
			fileChooser.setCurrentDirectory(new File(Config.ConfigItem.ACTIVE_FOLDER.getValueString()));
		}
		if (fInput == null) {
			fileChooser.setDialogTitle(FILE_OPEN.getLabel());
			int ret = fileChooser.showOpenDialog(desktop);
			if (JFileChooser.APPROVE_OPTION == ret) {
				File file = fileChooser.getSelectedFile();
				try {
					activeFile = file;
					String ext = CommonFileFilter.getExtension(activeFile
							.getName());
					if (ext.equalsIgnoreCase("xls")) {
						JOptionPane
								.showMessageDialog(desktop,
										"Importing project from excel file was not supported yet");
					} else {
						FileReader in = new FileReader(file);
						p = ProjectIO.loadData(in);
					}
				} catch (Exception e) {
					JOptionPane.showMessageDialog(
							desktop,
							APP_FILE.getAccessibleDescription() + "\r\n"
									+ e.getMessage(), APP_FILE.getLabel(),
							JOptionPane.ERROR_MESSAGE);
				}
			}
		} else {
			try {
				FileReader in = new FileReader(fInput);
				// Reader in = new InputStreamReader(new
				// FileInputStream(fInput), "UTF-8");
				p = ProjectIO.loadData(in);
			} catch (Exception e) {
				JOptionPane.showMessageDialog(
						desktop,
						APP_FILE.getAccessibleDescription() + "\r\n"
								+ e.getMessage(), APP_FILE.getLabel(),
						JOptionPane.ERROR_MESSAGE);
			}
		}
		if (p != null) {
			projectManager.add(p, activeFile);
		}

		DockableState state = desktop.getDockableState(propertyDock);
		if (state.isMaximized()) {
			desktop.restore(propertyDock);
		}
	}

	public void removeProject(Project p, Component c) {
		if(p==null) return;
		int ret = JOptionPane.showConfirmDialog(c,
				APP_CLOSE.getAccessibleDescription() + ": " + p.getLabel()
						+ "?", APP_CLOSE.getLabel(), JOptionPane.YES_NO_OPTION);
		if (ret == JOptionPane.YES_OPTION)
			projectManager.remove(p);
	}

	private void saveCalculationOutput(Project p) {
		if (activeFile != null) {
			fileChooserOutput.setCurrentDirectory(activeFile.getParentFile());
		}

		int ret = fileChooserOutput.showSaveDialog(desktop);
		if (JFileChooser.APPROVE_OPTION == ret) {
			activeFile = CommonFileFilter.getValidatedFile(fileChooserOutput,
					defaultFileFilter);
			if (activeFile.exists()) {
				ret = JOptionPane.showConfirmDialog(desktop,
						APP_SAVE.getAccessibleDescription(),
						APP_SAVE.getLabel(), JOptionPane.YES_NO_OPTION,
						JOptionPane.WARNING_MESSAGE);
				if (ret == JOptionPane.NO_OPTION) {
					return;
				}
			}
		} else {
			return;
		}

		try {
			// TODO: pake progress bar dari sini

			String ext = CommonFileFilter.getExtension(activeFile.getName());
			if (ext.equalsIgnoreCase("xls")) {
				ProjectIO.saveCalculationOutputToExcel(p, activeFile);
			} else {
				ProjectIO.saveCalculationOutput(p, activeFile);
			}

		} catch (Exception e) {
			// e.printStackTrace();
			JOptionPane.showMessageDialog(
					desktop,
					APP_WRITE.getAccessibleDescription() + "\r\n"
							+ e.getMessage(), APP_WRITE.getLabel(),
					JOptionPane.ERROR_MESSAGE);
		}

	}

	private void saveProject(Project p, boolean save_as) {
		File fp = null;
		if (!save_as)
			fp = projectManager.getFile(p);
		if (fp == null) {
			if (activeFile != null) {
				fileChooser.setCurrentDirectory(activeFile.getParentFile());
			}
			fileChooser.setDialogTitle(FILE_SAVE_AS.getLabel());
			int ret = fileChooser.showSaveDialog(desktop);
			if (JFileChooser.APPROVE_OPTION == ret) {
				activeFile = CommonFileFilter.getValidatedFile(fileChooser,
						defaultFileFilter);
				if (activeFile.exists()) {
					ret = JOptionPane.showConfirmDialog(desktop,
							APP_SAVE.getAccessibleDescription(),
							APP_SAVE.getLabel(), JOptionPane.YES_NO_OPTION,
							JOptionPane.WARNING_MESSAGE);
					if (ret == JOptionPane.NO_OPTION) {
						return;
					}
				}
			} else {
				return;
			}
		} else {
			activeFile = fp;
		}

		try {
			// TODO: pake progress bar dari sini

			String ext = CommonFileFilter.getExtension(activeFile.getName());
			if (ext.equalsIgnoreCase("xls")) {
				ProjectIO.saveProjectToExcel(p, activeFile);
				int ret = JOptionPane.showConfirmDialog(desktop,
						APP_SAVE_ALL.getAccessibleDescription(),
						APP_SAVE_ALL.getLabel(), JOptionPane.YES_NO_OPTION,
						JOptionPane.QUESTION_MESSAGE);
				if (ret == JOptionPane.YES_OPTION) {
					ProjectIO.saveCalculationOutputToExcel(p, activeFile);
				}
				projectManager.putFile(p, null);
			} else {
				ProjectIO.saveProject(p, activeFile);
				projectManager.putFile(p, activeFile);
			}

		} catch (Exception e) {
			// e.printStackTrace();
			JOptionPane.showMessageDialog(
					desktop,
					APP_WRITE.getAccessibleDescription() + "\r\n"
							+ e.getMessage(), APP_WRITE.getLabel(),
					JOptionPane.ERROR_MESSAGE);
		}

	}

	protected void showPropertyDock(Object item, MenuAdapter pi, Project project) {
		switchPropertyDisplay(item, pi, project);
		if (propertyDock.getDockKey().getLocation() == Location.CLOSED) {
			if (desktop.getContext()
					.getDockablesByState(desktop, Location.DOCKED).size() == 1) {
				desktop.split(projectDock, propertyDock,
						DockingConstants.SPLIT_RIGHT);
			} else {
				desktop.addDockable(propertyDock, new RelativeDockablePosition(
						0.6, 0.1, 0.5, 1));
			}
		}
	}

	protected void switchPropertyDisplay(Object item, MenuAdapter pi,
			Project project) {
		propertyDock.showProperty(item, pi, project);
	}

	public boolean isLinkedMatrixTab() {
		return true;
	}

	public StatusBar getStatusBar() {
		return statusBar;
	}
	
	public static void openExtFile(File document) throws IOException {
	    Desktop dt = Desktop.getDesktop();
	    dt.open(document);
	}
}
